package com.compty.core.processor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.compty.core.framework.context.spring.SpringContextHolder;
import com.compty.core.framework.context.webcontext.ThreadContextHolder;
import com.compty.core.framework.util.EopSetting;
import com.compty.core.framework.util.JspUtil;
import com.compty.paint.util.Constants;
import com.compty.security.domain.User;
import com.compty.security.manager.UserManager;

/**
 * @author 朱庆辉
 * @version 1.0
 */
public class DispatcherFilter implements Filter {
	
    /**
     * ant路径匹配器.
     */
    private final PathMatcher pathMatcher = new AntPathMatcher();

    /**
     * perl5匹配器.
     */
    private final PatternMatcher matcher = new Perl5Matcher();
    
    private UserManager userManager = null;
    
    private List<String> urls = null; //受保护资源路径集合
	
	/**
	 * The default character encoding to set for requests that pass through this
	 * filter.
	 */
	protected String encoding = "";

	/**
	 * The filter configuration object we are associated with. If this value is
	 * null, this filter instance is not currently configured.
	 */
	protected FilterConfig filterConfig = null;

	/**
	 * Should a character encoding specified by the client be ignored?
	 */
	protected boolean ignore = true;
	
	/**
	 * Take this filter out of service.
	 */
	public void destroy() {

		this.encoding = null;
		this.filterConfig = null;

	}

	public void init(FilterConfig filterConfig) {
		this.filterConfig = filterConfig;
		this.encoding = filterConfig.getInitParameter("encoding");
		String value = filterConfig.getInitParameter("ignore");
		if (value == null)
			this.ignore = true;
		else if (value.equalsIgnoreCase("true"))
			this.ignore = true;
		else if (value.equalsIgnoreCase("yes"))
			this.ignore = true;
		else
			this.ignore = false;
		
		userManager = SpringContextHolder.getBean("userManager");
		urls = userManager.getResourcesByType("URL");

        //倒叙排序所有获取的url
        if (urls == null) {
            urls = new ArrayList<String>(0);
        }

        orderUrls(urls);
	}
	
	// ------------------------------------------------------ Protected Methods

	/**
	 * Select an appropriate character encoding to be used, based on the
	 * characteristics of the current request and/or filter initialization
	 * parameters. If no character encoding should be set, return
	 * <code>null</code>.
	 * <p>
	 * The default implementation unconditionally returns the value configured
	 * by the <strong>encoding</strong> initialization parameter for this
	 * filter.
	 * 
	 * @param request
	 *            The servlet request we are processing
	 */
	protected String selectEncoding(ServletRequest request) {
		return (this.encoding);

	}

	@SuppressWarnings("rawtypes")
	public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		
		try {
			
			String requestUri = httpRequest.getServletPath();
			
			if(requestUri.endsWith(".jsp") || requestUri.endsWith(".do")){//设置请求字符集及上下文的url
				
				if (ignore || (request.getCharacterEncoding() == null)) {
					String encodingtmp = selectEncoding(request);
					if (encodingtmp != null)
						request.setCharacterEncoding(encodingtmp);
						response.setCharacterEncoding(encodingtmp);
				}
				
				this.initContext(httpRequest, httpResponse);
			}
			
			//不验证注册登录url权限
			if(requestUri.startsWith("/p/")||requestUri.startsWith("/front/")||requestUri.startsWith("/commons/")||requestUri.startsWith("/js/")||requestUri.startsWith("/css/")
					||requestUri.startsWith("/images/")||requestUri.startsWith("/My97DatePicker/")
					||requestUri.endsWith(".jpg")||requestUri.endsWith("session-timeout.jsp")||requestUri.endsWith("accessDenied.jsp")
					||requestUri.endsWith(".ico")){
				chain.doFilter(request, response);
				return;
			}
			
			//不验证注册登录url权限
			if(requestUri.contains("/member/login.do")||requestUri.contains("/member/register.jsp")||requestUri.contains("/member/memberAction!register.do")
					||requestUri.contains("/member/memberAction!completeRegister.do")||requestUri.contains("/painter/painterAction!findCityByProvinceId.do")
					||requestUri.contains("/member/register_additional.jsp")){
				chain.doFilter(request, response);
				return;
			}
			
	        for (Iterator iterator = urls.iterator(); iterator.hasNext();) {
	            String resString = (String) iterator.next();

	            //以首先匹配成功的资源进行鉴权
	            if (isResourceMatch(true, requestUri, resString)) {
	    			//检查是否登陆用户
	    			if(!isLogin(httpRequest)){
	    				String  context = httpRequest.getContextPath();
	    				httpResponse.sendRedirect(context+"/session-timeout.jsp");
	    				return;
	    			}
	    			
	    			User user = getLoninUser(httpRequest);
	    			
	    			String[] authorities = userManager.getPreviligesByUser(user.getUsername());//当前登录用户权限集合
	    			
	    			//验证是否有权限
	    			if(!hasRole(resString, authorities)){
	    				String  context = httpRequest.getContextPath();
	    				httpResponse.sendRedirect(context+"/accessDenied.jsp");
	    				return;
	    			}
	            }
	        }
			
			// Pass control on to the next filter
			chain.doFilter(request, response);
			
			return ;
		
		} catch (RuntimeException exception) {
			exception.printStackTrace();
			request.setAttribute("message", exception.getMessage());
			String content = JspUtil.getJspOutput("/commons/error.jsp",httpRequest, httpResponse);
			response.getWriter().print(content);
			// response.flushBuffer();
		}
	}

	private void initContext(HttpServletRequest httpRequest,
			HttpServletResponse httpResponse) {
		/**
		 * 将requst response及静态资源域名加入到上下文
		 */
		HttpSession session = httpRequest.getSession();
		ThreadContextHolder.getSessionContext().setSession(session);
		ThreadContextHolder.setHttpRequest(httpRequest);
		ThreadContextHolder.setHttpResponse(httpResponse);
		httpRequest.setAttribute("staticserver", EopSetting.IMG_SERVER_DOMAIN);
		httpRequest.setAttribute("ext", EopSetting.EXTENSION);
		
	}
	
	/**
	* @Description: 得到当前登录用户
	* @return User
	* @throws
	 */
	private User getLoninUser(HttpServletRequest request){
 		
		return (User) request.getSession().getAttribute(Constants.Userinfo);
	}
	
	/**
	* @Description: 判断用户是否已登录
	* @return boolean
	* @throws
	 */
	private boolean isLogin(HttpServletRequest request) {
	 	
		User user = (User) request.getSession().getAttribute(Constants.Userinfo);
		
		return user!=null;
	}
	
	/**
	* @Description: 是否有权限
	* @param resString 当前请求资源
	* @param authorities 当前登录用户资源权限数组
	* @return boolean true 有权限 false无权限
	 */
	private boolean hasRole(String resString,String[] authorities){
		
		boolean rv = false;
        for (int i = 0; authorities != null && i < authorities.length; i++) {
			if(resString.equals(authorities[i])){
				rv = true;
				break;
			}
		}
		
		return rv;
	}
	
    /**
     * 查看当前url和资源中的url是否匹配.
     * @param isUseAntPath Boolean 是否使用ant路径匹配
     * @param runningUrl String 当前请求url
     * @param rescUrl String 资源url
     * @return boolean
     */
    private boolean isResourceMatch(Boolean isUseAntPath,String runningUrl, String rescUrl) {
        if (isUseAntPath) {
            return pathMatcher.match(rescUrl, runningUrl);
        } else {
            Pattern compiledPattern;
            Perl5Compiler compiler = new Perl5Compiler();

            try {
                compiledPattern = compiler.compile(rescUrl,
                        Perl5Compiler.READ_ONLY_MASK);
            } catch (MalformedPatternException mpe) {
                throw new IllegalArgumentException(
                    "Malformed regular expression: " + rescUrl, mpe);
            }

            return matcher.matches(runningUrl, compiledPattern);
        }
    }
	
    /**
     * 把url资源按倒序排序.
     * @param urls url list
     */
    private void orderUrls(List<String> urls) {
        Collections.sort(urls);
        Collections.reverse(urls);
    }

}